<html>
    <head>
        <title>IQueryable接口的异步操作</title>
    </head>
    <body>
        <script>
            /*
                        四、IQueryable<T>（可查询接口)的异步操作

                                    IRepository提供了GetQueryableAsync()来获得一个IQueryable，这意味着你可以直接在上面使用LINQ扩展方法，如上面“Quering/LINQ over the Repositories”部分的示例所示。

                                    eg： 使用Where(...)和ToList()扩展方法

                                            //  获取 IQueryable<Person>对象
                                            var queryable = await _personRepository.GetQueryableAsync();
                                            var people = queryable
                                                .Where(p => p.Name.Contains(nameFilter))
                                                .ToList();

                                    .ToList，Count()…是System.Linq命名空间中定义的标准扩展方法（请参下面地址文档）: 
                                    
                                            https://learn.microsoft.com/en-us/dotnet/api/system.linq.queryable?view=net-9.0

                                    相反，你通常希望使用.ToListAsync()，.CountAsync()…，能够编写真正的异步代码。

                                    但是，当你使用标准应用程序启动模板创建新项目时，你会看到你不能使用"应用程序层"或"领域层"中的所有异步扩展方法，因为：

                                            ①、这些异步方法不是标准的LINQ方法，是EF Core 对 IQueryable 的扩展方法，它们在Microsoft. EntityFrameworkCoreNuGet 包中定义。

                                            ②、标准模板没有从领域层和应用程序层引用到EF Core包，以独立于数据库提供程序。

                                    
                                    根据你的需求和开发模型，你有以下选项可以使用异步方法:



                                1、在领域层或应用程引用数据库提供程序员NuGet包

                                                最简单的解决方案是直接从你要使用这些异步方法的项目中添加EF Core包。

                                                将 "Volo. Abp.EntityFrameworkCore" NuGet包添加到你的项目中，该项目间接引用EF Core包。

                                                这可确保你使用与应用程序其余部分兼容的正确版本的EF Core。

                                                当你将NuGet包添加到你的项目时，你可以充分利用EF Core扩展方法。


                                                eg: 添加EF Core包后直接使用ToListAsync()

                                                        var queryable = await _personRepository.GetQueryableAsync();
                                                        var people = queryable
                                                            .Where(p => p.Name.Contains(nameFilter))
                                                            .ToListAsync();


                                                建议采用这种方法:

                                                        如果你正在开发一个应用程序并且你不打算在未来更改EF Core，或者如果你以后需要更改它，你可以容忍。
                                                        
                                                        我们相信如果你正在开发最终应用程序，这是合理的。


                                2、使用IRepository（默认存储库的扩展方法）异步扩展方法

                                                ABP为存储库提供异步扩展方法，类似于异步LINQ扩展方法。

                                                eg： 在存储库上使用CountAsync和FirstOrDefaultAsync方法

                                                    
                                                        var countAll = await _personRepository
                                                            .CountAsync();

                                                        var count = await _personRepository
                                                            .CountAsync(x => x.Name.StartsWith("A"));

                                                        var book1984 = await _bookRepository
                                                            .FirstOrDefaultAsync(x => x.Name == "John");  

                                             默认存储库对象上直接支持标准的Linq查询：

                                                        AllAsync，
                                                        AnyAsync，
                                                        AverageAsync，
                                                        ContainsAsync，
                                                        CountAsync，
                                                        FirstAsync，
                                                        FirstOrDefaultAsync，
                                                        LastAsync，
                                                        LastOrDefaultAsync，
                                                        LongCountAsync，
                                                        MaxAsync，
                                                        MinAsync，
                                                        SingleAsync，
                                                        SingleOrDefaultAsync，
                                                        SumAsync，
                                                        ToArrayAsync，
                                                        ToListAsync


                                            这种方法仍然有一个限制。它是基于默认存储库的功能，不能在IQueryable对象上使用。

                                            eg:  // 这个例子中的使用方法是不支持的：

                                                        var queryable = await _bookRepository.GetQueryableAsync();
                                                        var count = await queryable.Where(x => x.Name.Contains("A")).CountAsync(); // 不支持


                                                这是因为此示例中的CountAsync()方法是在IQueryable接口上调用的，而不是在存储库对象上。

                                                

                                        尽可能建议使用此方法



                               3、使用IAsyncQueryableExecuter

                                            IAsyncQueryableExecuter是一个服务，用于异步执行IQueryable<T>对象，而"不依赖于实际的数据库提供程序"。

                                            eg: 注入并使用IAsyncQueryableExecuter.ToListAsync()方法

                                                        using System;
                                                        using System.Collections.Generic;
                                                        using System.Linq;
                                                        using System.Threading.Tasks;
                                                        using Volo.Abp.Application.Dtos;
                                                        using Volo.Abp.Application.Services;
                                                        using Volo.Abp.Domain.Repositories;
                                                        using Volo.Abp.Linq;

                                                        namespace AbpDemo
                                                        {
                                                            public class ProductAppService : ApplicationService, IProductAppService
                                                            {
                                                                private readonly IRepository<Product, Guid> _productRepository;
                                                                private readonly IAsyncQueryableExecuter _asyncExecuter;  // 异步Queryable执行器服务注入


                                                                public ProductAppService(
                                                                    IRepository<Product, Guid> productRepository,
                                                                    IAsyncQueryableExecuter asyncExecuter)
                                                                {
                                                                    _productRepository = productRepository;
                                                                    _asyncExecuter = asyncExecuter;
                                                                }

                                                                public async Task<ListResultDto<ProductDto>> GetListAsync(string name)
                                                                {
                                                                    // 获取 IQueryable<T>
                                                                    var queryable = await _productRepository.GetQueryableAsync();
                                                                    
                                                                    // 创建查询
                                                                    var query = queryable
                                                                        .Where(p => p.Name.Contains(name))
                                                                        .OrderBy(p => p.Name);

                                                                    // 异步执行IQueryable<T>对象，并获取结果列表
                                                                    List<Product> products = await _asyncExecuter.ToListAsync(query);

                                                                    //...
                                                                }
                                                            }
                                                        }


                                            ApplicationService和DomainService基类已经预先注入了AsyncExecuter属性，无需显式构造函数注入即可使用。

                                            ABP使用实际数据库提供者的API异步执行查询。虽然这不是执行查询的常用方式，但它是在不依赖数据库提供者的情况下使用异步API的最佳方式。

                                            建议采用这种方法的原因：

                                                    如果你想在不依赖数据库提供程序的情况下开发应用程序代码。

                                                    如果你正在构建一个可重用库，它没有数据库提供程序集成包，但在某些情况下需要执行IQueryable<T>对象。

                               

                                4、自定义存储方法

                                          你始终可以创建自定义存储库方法并使用特定于数据库提供程序的API，例如此处的异步扩展方法。有关自定义存储库的更多信息，请参阅EF Core文档。
                                          
                                          
                                          建议采用这种方法的原因：

                                                  如果你想完全隔离你的领域层和应用程序层与数据库提供程序的话，这样很好。
                                                  
                                                  如果你开发了一个可重用的应用程序模块并且不想强制使用特定的数据库提供程序，这应该作为最佳实践来完成。

                    */
        </script>
    </body>
</html>